//
// Created by hjie on 23-7-18.
//

#include "base_util.h"
#include "input_output_operate.h"
#include "message_queue.h"
#include <cstdio>
#include <libavutil/frame.h>

void PrintLogInfo(const std::string & file, const std::string & function, int line, int error_code)
{
    const size_t length = file.size() + function.size() + 8 + 8 + 1024;
    char * buffer;
    buffer = new char [length];
    bzero(buffer, length);
    snprintf(buffer, length, "file : %s | line : %d | function : %s | content \n error code : %d and error message : %s.\n",
             file.c_str(),
             line,
             function.c_str(),
             error_code,
             GetErrorMessage(error_code).c_str());

    printf("%s\n",buffer);

    delete [] buffer;
    buffer = nullptr;
}

void PrintLogInfo(const std::string & file, const std::string & function, int line, const std::string & message)
{
    const size_t length = file.size() + function.size() + 8 + message.size() + 100;
    char * buffer;
    buffer = new char [length];
    bzero(buffer, length);
    snprintf(buffer, length, "file : %s | line : %d | function : %s | content : \n%s.\n",
             file.c_str(),
             line,
             function.c_str(),
             message.c_str());

    printf("%s\n",buffer);

    delete [] buffer;
    buffer = nullptr;
}


int DecoderAudioOrVideoData(AVCodecContext * codec_context, AVPacket * packet, std::vector<AVFrame*> & frame_vec, bool is_transfer)
{
    int result = avcodec_send_packet(codec_context, packet);
    if (result < 0)
    {
        return -1;
    }
    while (result >= 0)
    {
        AVFrame * frame = av_frame_alloc();
        result = avcodec_receive_frame(codec_context, frame);
        if (result != 0)
        {
            if (result == AVERROR(EAGAIN) || result == AVERROR_EOF)
            {
                result = 0;
            }
            else
            {
                result = -1;
            }
            break;
        }
        if (codec_context->opaque != nullptr)
        {
            GetFormatForOutputHwFrameCtx(codec_context);
        }
        if ((codec_context->hw_device_ctx || codec_context->hw_frames_ctx) && is_transfer)
        {
            AVFrame * tmp = av_frame_alloc();
            result = av_hwframe_transfer_data(tmp, frame, 0);
            if (result == 0)
            {
                tmp->pkt_duration = frame->pkt_duration;
                tmp->pkt_dts      = frame->pkt_dts;
                tmp->pts          = frame->pts;
                tmp->pict_type    = frame->pict_type;
                frame_vec.push_back(tmp);
                av_frame_unref(frame);
                continue;
            }
            else
            {
                result = 0;
                av_frame_free(&tmp);
                break;
            }
        }
        else
        {
            frame_vec.push_back(frame);
        }
    }
    return result;
}

int EncoderAudioOrVideoData(AVCodecContext * codec_context, AVFrame * frame, std::vector<AVPacket*> & packet_vec)
{
    int result = avcodec_send_frame(codec_context, frame);
    if (result < 0)
    {
        char buffer[1024] = {0};
        av_make_error_string(buffer, 1024, result);
        printf("error code : %d and error msg : %s.\n", result, buffer);
        return -1;
    }
    while (true)
    {
        AVPacket * packet = av_packet_alloc();
        result = avcodec_receive_packet(codec_context, packet);
        if (result < 0)
        {
            if (result == AVERROR(EAGAIN) || result == AVERROR_EOF)
            {
                result = 0;
            }
            av_packet_free(&packet);
            break;
        }
        av_packet_free(&packet);
    }
    return result;
}

void ReleaseAVFormatCtx(AVFormatContext ** format_context)
{
    if (*format_context)
    {
        avformat_close_input(format_context);
        avformat_free_context(*format_context);
        *format_context = nullptr;
    }
}

std::string GetErrorMessage(int error_code)
{
    char buff[1024] = {0};
    av_make_error_string(buff, sizeof(buff), error_code);
    return buff;
}

int DecoderPacketData(AVCodecContext * codec_context, AVPacket * packet, FrameMessageQueue & queue)
{
    int result = avcodec_send_packet(codec_context, packet);
    if (result < 0)
    {
        PrintLog(result);
        return result;
    }
    while (true)
    {
        AVFrame * frame = av_frame_alloc();
        if (codec_context->codec_type == AVMEDIA_TYPE_VIDEO)
        {
            frame->width  = codec_context->width;
            frame->height = codec_context->height;
            frame->format = codec_context->pix_fmt;
            av_frame_get_buffer(frame, 0);
        }
        result = avcodec_receive_frame(codec_context, frame);
        if (result < 0)
        {
            if (result == AVERROR(EAGAIN) || result == AVERROR_EOF)
            {
                result = 0;
            }
            else
            {
                PrintLog(result);
            }
            av_frame_free(&frame);
            break;
        }
        else
        {
            if (codec_context->opaque != nullptr)
            {
                GetFormatForOutputHwFrameCtx(codec_context);
            }
            queue.PushFrame(&frame);
        }
    }
    return result;
}

int DecoderPacketDataAndTransfer(AVCodecContext * codec_context, AVPacket * packet, FrameMessageQueue & queue)
{
    int result = avcodec_send_packet(codec_context, packet);
    if (result < 0)
    {
        PrintLog(result);
        return result;
    }
    while (true)
    {
        AVFrame * frame = av_frame_alloc();
        result = avcodec_receive_frame(codec_context, frame);
        if (result < 0)
        {
            if (result == AVERROR(EAGAIN) || result == AVERROR_EOF)
            {
                result = 0;
            }
            else
            {
                PrintLog(result);
            }
            av_frame_free(&frame);
            break;
        }
        else
        {
            if (codec_context->opaque != nullptr)
            {
                GetFormatForOutputHwFrameCtx(codec_context);
            }
            if (codec_context->hw_frames_ctx || codec_context->hw_device_ctx)
            {
                AVFrame * tmp = av_frame_alloc();
                result = av_hwframe_transfer_data(tmp, frame, 0);
                if (result == 0)
                {
                    tmp->pkt_duration = frame->pkt_duration;
                    tmp->pkt_dts      = frame->pkt_dts;
                    tmp->pts          = frame->pts;
                    tmp->pict_type    = frame->pict_type;

                    av_frame_copy_props(tmp, frame);

                    queue.PushFrame(&tmp);
                    av_frame_unref(frame);
                    av_frame_free(&frame);
                    continue;
                }
                else
                {
                    result = 0;
                    av_frame_free(&tmp);
                    av_frame_unref(frame);
                    av_frame_free(&frame);
                    break;
                }
            }
            else
            {
                queue.PushFrame(&frame);
            }
        }
    }
    return result;
}

int EncoderFrameData(AVCodecContext * codec_context, AVFrame * frame,  PacketMessageQueue & queue)
{
    int result = avcodec_send_frame(codec_context, frame);
    if (result < 0)
    {
        PrintLog(result);
        return result;
    }
    while (true)
    {
        AVPacket * packet = av_packet_alloc();
        result = avcodec_receive_packet(codec_context, packet);
        if (result < 0)
        {
            if (result == AVERROR(EAGAIN) || result == AVERROR_EOF)
            {
                result = 0;
            }
            else
            {
                PrintLog(result);
            }
            av_packet_free(&packet);
            break;
        }
        else
        {
            queue.PushPacket(&packet);
        }
    }
    return result;
}

int ProcessPacketMessageQueue(AVCodecContext *codec_context, PacketMessageQueue &packet_queue, FrameMessageQueue &frame_queue)
{
    int result = 0;
    while (true)
    {
        AVPacket * packet = nullptr;
        result = packet_queue.PopPacketNonBlocking(&packet);
        if (result < 0)
        {
            if (result == AVERROR(EAGAIN))
            {
                result = 0;
            }
            break;
        }
        result = DecoderPacketData(codec_context, packet, frame_queue);
        if (result < 0)
        {
            av_packet_unref(packet);
            av_packet_free(&packet);
            break;
        }
        av_packet_unref(packet);
        av_packet_free(&packet);
    }
    return result;
}


int ProcessFrameMessageQueue(AVCodecContext * codec_context, FrameMessageQueue & frame_queue, PacketMessageQueue & packet_queue)
{
    int result = 0;
    while (true)
    {
        AVFrame * frame = nullptr;
        result = frame_queue.PopFrameNonBlocking(&frame);
        if (result < 0)
        {
            if (result == AVERROR(EAGAIN))
            {
                result = 0;
            }
            break;
        }
        result = EncoderFrameData(codec_context, frame, packet_queue);
        if (result < 0)
        {
            av_frame_unref(frame);
            av_frame_free(&frame);
            break;
        }
        av_frame_unref(frame);
        av_frame_free(&frame);
    }
    return result;
}

int ProcessNullptrPacket(AVCodecContext * codec_context, FrameMessageQueue & frame_queue)
{
    if (!codec_context)
    {
        return -1;
    }
    return DecoderPacketData(codec_context, nullptr, frame_queue);
}

int ProcessNullPtrPacketAndTransfer(AVCodecContext *codec_context, FrameMessageQueue &frame_queue)
{
    return DecoderPacketDataAndTransfer(codec_context, nullptr, frame_queue);
}

int ProcessNullptrFrame(AVCodecContext * codec_context,  FrameMessageQueue & frame_queue, PacketMessageQueue & packet_queue)
{
    int result = ProcessFrameMessageQueue(codec_context, frame_queue, packet_queue);
    if (result >= 0)
    {
        result = EncoderFrameData(codec_context, nullptr, packet_queue);
    }
    return result;
}


void ReadFrameToQueue(AVFormatContext *format_context, AVPacket *packet, PacketMessageQueue &packet_queue)
{
    int result = 0;
    while ((result = av_read_frame(format_context, packet)) >= 0)
    {
        AVPacket * copy_packet = av_packet_alloc();
        av_packet_move_ref(copy_packet, packet);
        packet_queue.PushPacket(&copy_packet);
        av_packet_unref(packet);
    }
    packet_queue.SetQueueEof();
}












